/*
 *  Copyright (C) 2020 original author or authors.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License
 *  is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 *  or implied. See the License for the specific language governing permissions and limitations under
 *  the License.
 */

package vip.justlive.easyproxy.core.protocol;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.charset.StandardCharsets;
import lombok.Data;
import lombok.experimental.Accessors;
import vip.justlive.oxygen.core.exception.Exceptions;

/**
 * 消息体
 *
 * @author wubo
 */
@Data
@Accessors(chain = true)
public class ProxyData {

  /**
   * 消息头基础长度（类型+数据大小）
   */
  public static final int BASE_LENGTH = 12;

  private int id;
  /**
   * 类型
   */
  private ProxyDataType type;
  /**
   * 请求体
   */
  private byte[] body;

  public static ProxyData auth(String key) {
    return new ProxyData().setType(ProxyDataType.AUTH)
        .setBody(key.getBytes(StandardCharsets.UTF_8));
  }

  public static ProxyData connect(String address, int id, long channelId) {
    ProxyData pd = new ProxyData().setType(ProxyDataType.CONNECT).setId(id);
    pd.serialize(
        new ChannelData().setId(channelId).setData(address.getBytes(StandardCharsets.UTF_8)));
    return pd;
  }

  public static ProxyData transfer(byte[] data, int id, long channelId) {
    ProxyData pd = new ProxyData().setType(ProxyDataType.TRANSFER).setId(id);
    pd.serialize(new ChannelData().setId(channelId).setData(data));
    return pd;
  }

  public static ProxyData closeChannel(int id, long channelId) {
    ProxyData pd = new ProxyData().setType(ProxyDataType.CLOSE_CHANNEL).setId(id);
    pd.serialize(new ChannelData().setId(channelId));
    return pd;
  }

  public static ProxyData closeProxy(int id) {
    return new ProxyData().setType(ProxyDataType.CLOSE_PROXY).setId(id);
  }

  public static ProxyData close() {
    return new ProxyData().setType(ProxyDataType.CLOSE);
  }

  public String bodyAsString() {
    if (body != null) {
      return new String(body, StandardCharsets.UTF_8);
    }
    return null;
  }

  private void serialize(ChannelData cd) {
    try (ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream output = new ObjectOutputStream(
        out)) {
      output.writeObject(cd);
      body = out.toByteArray();
    } catch (IOException e) {
      throw Exceptions.wrap(e);
    }
  }

  public ChannelData deserialize() {
    try (ByteArrayInputStream in = new ByteArrayInputStream(
        body); ObjectInputStream input = new ObjectInputStream(in)) {
      return (ChannelData) input.readObject();
    } catch (IOException | ClassNotFoundException e) {
      throw Exceptions.wrap(e);
    }
  }
}
